package uk.ac.ebi.bioinvindex.search.hibernatesearch;

import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Restrictions;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import uk.ac.ebi.bioinvindex.model.impl.StudyImpl;
import uk.ac.ebi.bioinvindex.search.SearchException;
import uk.ac.ebi.bioinvindex.search.StudyFreeTextSearch;

import javax.persistence.EntityManager;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * User: Nataliya Sklyar (nsklyar@ebi.ac.uk)
 * Date: Sep 14, 2007
 */
public class StudyFreeTextSearchImpl implements StudyFreeTextSearch {

	protected EntityManager entityManager;

	public List<StudyImpl> searchAllFields(String searchPattern) throws SearchException {
		List list;

		try {
			org.apache.lucene.search.Query luceneQuery = buildQuery(searchPattern);
			FullTextSession session = getSession();
			list = session.createFullTextQuery(luceneQuery, StudyImpl.class).list();
		} catch (ParseException e) {
			throw new SecurityException("Exception when parsing query " + searchPattern, e);
		}
		return list;
	}

	public List<Object[]> getIdsAllFields(String searchPattern) throws SearchException {
		List list;

		try {
			org.apache.lucene.search.Query luceneQuery = buildQuery(searchPattern);

			FullTextSession session = getSession();
			FullTextQuery fullTextQuery = session.createFullTextQuery(luceneQuery, StudyImpl.class);
			fullTextQuery.setProjection("id");
			list = fullTextQuery.list();
		} catch (ParseException e) {
			throw new SecurityException("Exception when parsing query " + searchPattern, e);
		}
		return list;
	}

	public List<StudyImpl> searchAndFilterByAssayType(String searchPattern, String assayType) throws SearchException {
		List list;

		try {
			org.apache.lucene.search.Query luceneQuery = buildQuery(searchPattern);
			FullTextSession session = getSession();
			FullTextQuery query = session.createFullTextQuery(luceneQuery, StudyImpl.class);
			query.setCriteriaQuery(buildCriteriaForAssayType(assayType));
			list = query.list();
		} catch (ParseException e) {
			throw new SecurityException("Exception when parsing query " + searchPattern, e);
		}
		return list;
	}

	//ToDo: externalize field names
	private org.apache.lucene.search.Query buildQuery(String searchPattern) throws ParseException {
		Map<String, Float> boostPerField = new HashMap<String, Float>();
		boostPerField.put("title", 2f);
		boostPerField.put("description", 1f);
		boostPerField.put("design_value", 1f);
		boostPerField.put("objective", 1f);
		boostPerField.put("assay_type_name", 1f);

		String[] productFields = {"title", "description", "design_value", "objective", "assay_type_name"};
		QueryParser parser = new MultiFieldQueryParser(productFields, new StandardAnalyzer(), boostPerField);
		parser.setAllowLeadingWildcard(true);
		org.apache.lucene.search.Query luceneQuery = parser.parse(searchPattern);
		return luceneQuery;
	}

	private Criteria buildCriteriaForAssayType(String assayTypeName) {
		Criteria criteria = getSession().createCriteria(StudyImpl.class);
		criteria
				.createCriteria("assays")
				.createCriteria("type")
				.add(Restrictions.eq("name", assayTypeName));

		return criteria;
	}

	private FullTextSession getSession() {
		FullTextSession session;
		Session deligate = (Session) entityManager.getDelegate();

		if (deligate instanceof FullTextSession) {
			session = (FullTextSession) deligate;
		} else {
			session = Search.createFullTextSession(deligate);
		}
		return session;
	}

	//////////////////////////
	// Dependencies injection
	public EntityManager getEntityManager() {
		if (entityManager == null) {
			throw new IllegalStateException("EntityManager has not been set on StudyFreeTextSearchImpl before usage");
		}
		return entityManager;

	}

	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
}
